<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/myblog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/myblog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/myblog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/myblog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/myblog/css/main.css">



<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.4/css/all.min.css" integrity="sha256-mUZM63G8m73Mcidfrv5E+Y61y7a12O5mW4ezU3bxqW4=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"gitee.com","root":"/myblog/","images":"/myblog/images","scheme":"Gemini","darkmode":false,"version":"8.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12},"copycode":false,"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/myblog/js/config.js"></script>
<meta name="description" content="CountDownLatch有以下两种使用场景 场景1. 让多个线程等待：模拟并发，让并发线程一起执行CountDownLatch充当一个发令枪的角色 12345678910111213141516CountDownLatch countDownLatch &#x3D; new CountDownLatch(1);for (int i &#x3D; 0; i &lt; 5; i++) &amp;#123;    new Th">
<meta property="og:type" content="article">
<meta property="og:title" content="并发编程">
<meta property="og:url" content="https://gitee.com/cmygis/2021/10/26/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/index.html">
<meta property="og:site_name" content="矿小测的博客">
<meta property="og:description" content="CountDownLatch有以下两种使用场景 场景1. 让多个线程等待：模拟并发，让并发线程一起执行CountDownLatch充当一个发令枪的角色 12345678910111213141516CountDownLatch countDownLatch &#x3D; new CountDownLatch(1);for (int i &#x3D; 0; i &lt; 5; i++) &amp;#123;    new Th">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://img-blog.csdnimg.cn/img_convert/fa4d24955103ee1c8c0564ab45eebe26.png">
<meta property="article:published_time" content="2021-10-26T13:54:09.000Z">
<meta property="article:modified_time" content="2021-10-31T14:13:33.574Z">
<meta property="article:author" content="矿小测">
<meta property="article:tag" content="原子操作">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img-blog.csdnimg.cn/img_convert/fa4d24955103ee1c8c0564ab45eebe26.png">


<link rel="canonical" href="https://gitee.com/cmygis/2021/10/26/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"https://gitee.com/cmygis/2021/10/26/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/","path":"2021/10/26/并发编程/","title":"并发编程"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>并发编程 | 矿小测的博客</title>
  




  <noscript>
    <link rel="stylesheet" href="/myblog/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/myblog/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">矿小测的博客</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu">
        <li class="menu-item menu-item-home"><a href="/myblog/" rel="section"><i class="home                          //首页 fa-fw"></i>首页</a></li>
        <li class="menu-item menu-item-archives"><a href="/myblog/archives/" rel="section"><i class="archive          //归档 fa-fw"></i>归档</a></li>
        <li class="menu-item menu-item-categories"><a href="/myblog/categories/" rel="section"><i class="th           //分类 fa-fw"></i>分类</a></li>
        <li class="menu-item menu-item-tags"><a href="/myblog/tags/" rel="section"><i class="tags                     //标签 fa-fw"></i>标签</a></li>
        <li class="menu-item menu-item-about"><a href="/myblog/about/" rel="section"><i class="user                   //关于 fa-fw"></i>关于</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#CountDownLatch"><span class="nav-number">1.</span> <span class="nav-text">CountDownLatch</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9C%BA%E6%99%AF1-%E8%AE%A9%E5%A4%9A%E4%B8%AA%E7%BA%BF%E7%A8%8B%E7%AD%89%E5%BE%85%EF%BC%9A%E6%A8%A1%E6%8B%9F%E5%B9%B6%E5%8F%91%EF%BC%8C%E8%AE%A9%E5%B9%B6%E5%8F%91%E7%BA%BF%E7%A8%8B%E4%B8%80%E8%B5%B7%E6%89%A7%E8%A1%8C"><span class="nav-number">1.1.</span> <span class="nav-text">场景1. 让多个线程等待：模拟并发，让并发线程一起执行</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9C%BA%E6%99%AF2-%E8%AE%A9%E5%8D%95%E4%B8%AA%E7%BA%BF%E7%A8%8B%E7%AD%89%E5%BE%85-%E5%A4%9A%E4%B8%AA%E7%BA%BF%E7%A8%8B%E4%BB%BB%E5%8A%A1%E5%AE%8C%E6%88%90%E5%90%8E%EF%BC%8C%E8%BF%9B%E8%A1%8C%E6%B1%87%E6%80%BB%E5%90%88%E5%B9%B6"><span class="nav-number">1.2.</span> <span class="nav-text">场景2 让单个线程等待:多个线程任务完成后，进行汇总合并</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CyclicBarrier"><span class="nav-number">2.</span> <span class="nav-text">CyclicBarrier</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#CountDownLatch%E4%B8%8ECyclicBarrier"><span class="nav-number">2.1.</span> <span class="nav-text">CountDownLatch与CyclicBarrier</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Semaphore"><span class="nav-number">3.</span> <span class="nav-text">Semaphore</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%EF%BC%9A"><span class="nav-number">3.1.</span> <span class="nav-text">使用场景：</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E9%97%B4%E4%BA%A4%E6%8D%A2%E6%95%B0%E6%8D%AEExchanger"><span class="nav-number">4.</span> <span class="nav-text">线程间交换数据Exchanger</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C%E7%B1%BB"><span class="nav-number">5.</span> <span class="nav-text">原子操作类</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B9%90%E8%A7%82%E9%94%81%E5%92%8C%E6%82%B2%E8%A7%82%E9%94%81"><span class="nav-number">6.</span> <span class="nav-text">乐观锁和悲观锁</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%82%B2%E8%A7%82%E9%94%81"><span class="nav-number">6.1.</span> <span class="nav-text">悲观锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B9%90%E8%A7%82%E9%94%81"><span class="nav-number">6.2.</span> <span class="nav-text">乐观锁</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CAS"><span class="nav-number">7.</span> <span class="nav-text">CAS</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JVM%E5%AF%B9CAS%E7%9A%84%E6%94%AF%E6%8C%81"><span class="nav-number">8.</span> <span class="nav-text">JVM对CAS的支持</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CAS%E7%BC%BA%E7%82%B9"><span class="nav-number">9.</span> <span class="nav-text">CAS缺点</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">10.</span> <span class="nav-text">总结</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">矿小测</p>
  <div class="site-description" itemprop="description">live and love</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/myblog/archives/">
          <span class="site-state-item-count">4</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/myblog/categories/">
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/myblog/tags/">
        <span class="site-state-item-count">2</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://gitee.com/cmygis/2021/10/26/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/myblog/images/avatar.gif">
      <meta itemprop="name" content="矿小测">
      <meta itemprop="description" content="live and love">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="矿小测的博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          并发编程
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-26 21:54:09" itemprop="dateCreated datePublished" datetime="2021-10-26T21:54:09+08:00">2021-10-26</time>
    </span>
      <span class="post-meta-item">
        <span class="post-meta-item-icon">
          <i class="far fa-calendar-check"></i>
        </span>
        <span class="post-meta-item-text">更新于</span>
        <time title="修改时间：2021-10-31 22:13:33" itemprop="dateModified" datetime="2021-10-31T22:13:33+08:00">2021-10-31</time>
      </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h2 id="CountDownLatch"><a href="#CountDownLatch" class="headerlink" title="CountDownLatch"></a>CountDownLatch</h2><p>有以下两种使用场景</p>
<h3 id="场景1-让多个线程等待：模拟并发，让并发线程一起执行"><a href="#场景1-让多个线程等待：模拟并发，让并发线程一起执行" class="headerlink" title="场景1. 让多个线程等待：模拟并发，让并发线程一起执行"></a>场景1. 让多个线程等待：模拟并发，让并发线程一起执行</h3><p>CountDownLatch充当一个发令枪的角色</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">CountDownLatch countDownLatch = <span class="keyword">new</span> CountDownLatch(<span class="number">1</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//准备完毕……运动员都阻塞在这，等待号令</span></span><br><span class="line">            countDownLatch.await();</span><br><span class="line">            String parter = <span class="string">&quot;【&quot;</span> + Thread.currentThread().getName() + <span class="string">&quot;】&quot;</span>;</span><br><span class="line">            System.out.println(parter + <span class="string">&quot;开始执行……&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Thread.sleep(<span class="number">2000</span>);<span class="comment">// 裁判准备发令</span></span><br><span class="line">countDownLatch.countDown();<span class="comment">// 发令枪：执行发令</span></span><br></pre></td></tr></table></figure>

<p>运行结果：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">【Thread-0】开始执行……</span><br><span class="line">【Thread-1】开始执行……</span><br><span class="line">【Thread-4】开始执行……</span><br><span class="line">【Thread-3】开始执行……</span><br><span class="line">【Thread-2】开始执行……</span><br></pre></td></tr></table></figure>

<p>我们通过CountDownLatch.await()，让多个参与者线程启动后阻塞等待，然后在主线程 调用CountDownLatch.countdown(1) 将计数减为0，让所有线程一起往下执行；<br>以此实现了多个线程在同一时刻并发执行，来模拟并发请求的目的</p>
<h3 id="场景2-让单个线程等待-多个线程任务完成后，进行汇总合并"><a href="#场景2-让单个线程等待-多个线程任务完成后，进行汇总合并" class="headerlink" title="场景2 让单个线程等待:多个线程任务完成后，进行汇总合并"></a>场景2 让单个线程等待:多个线程任务完成后，进行汇总合并</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">CountDownLatch countDownLatch = <span class="keyword">new</span> CountDownLatch(<span class="number">5</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> index = i;</span><br><span class="line">    <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span> + ThreadLocalRandom.current().nextInt(<span class="number">1000</span>));</span><br><span class="line">            System.out.println(<span class="string">&quot;finish&quot;</span> + index + Thread.currentThread().getName());</span><br><span class="line">            countDownLatch.countDown();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">countDownLatch.await();<span class="comment">// 主线程在阻塞，当计数器==0，就唤醒主线程往下执行。</span></span><br><span class="line">System.out.println(<span class="string">&quot;主线程:在所有任务运行完成后，进行结果汇总&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>CountDownLatch的不足</strong></p>
<p>CountDownLatch是一次性的，计算器的值只能在构造方法中初始化一次，之后没有任何机制再次对其设置值，当CountDownLatch使用完毕后，它不能再次被使用。</p>
<h2 id="CyclicBarrier"><a href="#CyclicBarrier" class="headerlink" title="CyclicBarrier"></a>CyclicBarrier</h2><p><img src="https://img-blog.csdnimg.cn/img_convert/fa4d24955103ee1c8c0564ab45eebe26.png" alt="img"></p>
<h3 id="CountDownLatch与CyclicBarrier"><a href="#CountDownLatch与CyclicBarrier" class="headerlink" title="CountDownLatch与CyclicBarrier"></a><strong>CountDownLatch与CyclicBarrier</strong></h3><p>CountDownLatch和CyclicBarrier都能够实现线程之间的等待，只不过它们侧重点不同：</p>
<ul>
<li>CountDownLatch一般用于一个或多个线程，等待其他线程执行完任务后，再才执行</li>
<li>CyclicBarrier一般用于一组线程互相等待至某个状态，然后这一组线程再同时执行</li>
</ul>
<h2 id="Semaphore"><a href="#Semaphore" class="headerlink" title="Semaphore"></a>Semaphore</h2><p>Semaphore信号量，可以用来控制同时访问特定资源的线程数量，通过协调各个线程，以保证合理的使用资源。</p>
<p>例如停车场的车位，每有一辆车进入停车场，车位就会减1，每有一辆车从停车场出去，车位就增加1，当剩余车位为0时，停车场就不准车辆进入，直到有一辆车从停车场出去为止。</p>
<h3 id="使用场景："><a href="#使用场景：" class="headerlink" title="使用场景："></a>使用场景：</h3><p>用于资源有明确访问数量限制的场景，常用于限流。</p>
<p>比如：数据库连接池，同时进行连接的线程有数量限制，连接不能超过一定的数量，当连接达到限制数量后，后面的线程只能排队等待前面的线程释放了数据库连接。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">acquire()</span><br><span class="line">    获取一个令牌，在获取到令牌、或者被其他线程调用中断之前一直处于阻塞状态。</span><br><span class="line">acquire(<span class="keyword">int</span> permits)  </span><br><span class="line">获取一个令牌，在获取到令牌、或者被其他线程调用中断、或超时之前线程一直处于阻塞状态。</span><br><span class="line">    </span><br><span class="line">acquireUninterruptibly() </span><br><span class="line">获取一个令牌，在获取到令牌之前线程一直处于阻塞状态（忽略中断）。</span><br><span class="line">    </span><br><span class="line">tryAcquire()</span><br><span class="line">尝试获得令牌，返回获取令牌成功或失败，不阻塞线程。</span><br><span class="line">​</span><br><span class="line">tryAcquire(<span class="keyword">long</span> timeout, TimeUnit unit)</span><br><span class="line">尝试获得令牌，在超时时间内循环尝试获取，直到尝试获取成功或超时返回，不阻塞线程。</span><br><span class="line">​</span><br><span class="line">release()</span><br><span class="line">释放一个令牌，唤醒一个获取令牌不成功的阻塞线程。</span><br><span class="line">​</span><br><span class="line">hasQueuedThreads()</span><br><span class="line">等待队列里是否还存在等待线程。</span><br><span class="line">​</span><br><span class="line">getQueueLength()</span><br><span class="line">获取等待队列里阻塞的线程数。</span><br><span class="line">​</span><br><span class="line">drainPermits()</span><br><span class="line">清空令牌把可用令牌数置为<span class="number">0</span>，返回清空令牌的数量。</span><br><span class="line">​</span><br><span class="line">availablePermits()</span><br><span class="line">返回可用的令牌数量。</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestCar</span> </span>&#123;</span><br><span class="line">    <span class="comment">//停车场同时容纳的车辆10</span></span><br><span class="line">    <span class="keyword">private</span>  <span class="keyword">static</span>  Semaphore semaphore=<span class="keyword">new</span> Semaphore(<span class="number">10</span>);</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//模拟100辆车进入停车场</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i&lt;<span class="number">100</span>;i++)&#123;</span><br><span class="line">            Thread thread=<span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;====&quot;</span>+Thread.currentThread().getName()+<span class="string">&quot;来到停车场&quot;</span>);</span><br><span class="line">                        <span class="keyword">if</span>(semaphore.availablePermits()==<span class="number">0</span>)&#123;</span><br><span class="line">                            System.out.println(<span class="string">&quot;车位不足，请耐心等待&quot;</span>);</span><br><span class="line">                        &#125;</span><br><span class="line">                        semaphore.acquire();<span class="comment">//获取令牌尝试进入停车场</span></span><br><span class="line">                        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;成功进入停车场&quot;</span>);</span><br><span class="line">                        Thread.sleep(<span class="keyword">new</span> Random().nextInt(<span class="number">10000</span>));<span class="comment">//模拟车辆在停车场停留的时间</span></span><br><span class="line">                        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;驶出停车场&quot;</span>);</span><br><span class="line">                        semaphore.release();<span class="comment">//释放令牌，腾出停车场车位</span></span><br><span class="line">                    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;,i+<span class="string">&quot;号车&quot;</span>);</span><br><span class="line">            thread.start();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/98593407">https://zhuanlan.zhihu.com/p/98593407</a></p>
<p>释放令牌成功之后，将队列中的第一个需要唤醒的线程进行唤醒</p>
<h2 id="线程间交换数据Exchanger"><a href="#线程间交换数据Exchanger" class="headerlink" title="线程间交换数据Exchanger"></a><strong>线程间交换数据Exchanger</strong></h2><p>Exchanger是用于线程间数据交换的工具类<br>它提供一个同步点 ,在这个同步点两个线程可以通过exchange彼此交换数据<br>第一个线程先执行exchange()方法，它会一直等待第二个线程也<br>执行exchange方法，当两个线程都到达同步点时，这两个线程就可以交换数据，将本线程生产出来的数据传递给对方。<br>————————————————<br>版权声明：本文为CSDN博主「呆大王」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。<br>原文链接：<a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_50695280/article/details/115099872">https://blog.csdn.net/qq_50695280/article/details/115099872</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.Exchanger;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExchangerTest</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Exchanger&lt;String&gt; ex = <span class="keyword">new</span> Exchanger&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ExecutorService executorService = Executors.newCachedThreadPool();</span><br><span class="line">        executorService.execute(()-&gt; &#123;</span><br><span class="line">            String a = <span class="string">&quot;小红&quot;</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                ex.exchange(a);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        executorService.execute(()-&gt; &#123;</span><br><span class="line">            String b = <span class="string">&quot;小明&quot;</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                String exchange = ex.exchange(b);</span><br><span class="line">                System.out.println(<span class="string">&quot;得到了: &quot;</span>+exchange);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        executorService.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">-----------------------------------------</span><br><span class="line">输出：</span><br><span class="line">    得到了：小红</span><br></pre></td></tr></table></figure>



<h2 id="原子操作类"><a href="#原子操作类" class="headerlink" title="原子操作类"></a><strong>原子操作类</strong></h2><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_30307921/article/details/98445676">https://blog.csdn.net/weixin_30307921/article/details/98445676</a></p>
<h2 id="乐观锁和悲观锁"><a href="#乐观锁和悲观锁" class="headerlink" title="乐观锁和悲观锁"></a>乐观锁和悲观锁</h2><h3 id="悲观锁"><a href="#悲观锁" class="headerlink" title="悲观锁"></a>悲观锁</h3><p>悲观锁：认为别的线程会修改值</p>
<p>独占锁是一种悲观锁，synchronized就是一种独占锁。synchronized加锁后能够确保程序执行时不会被其他线程干扰，得到正确的结果。</p>
<h3 id="乐观锁"><a href="#乐观锁" class="headerlink" title="乐观锁"></a>乐观锁</h3><p>乐观锁：认为别的线程不会去修改值。如果发现值被修改了，可以再次重试。CAS机制（Compare and Swap)就是一种乐观锁。</p>
<h2 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a>CAS</h2><p><code>CAS</code>是一种无锁（lock-free)算法。也是一种现代CPU广泛支持的CPU指令级的操作，只有一步原子操作，所以非常快。而且CAS避免了请求操作系统来裁定锁的问题，不用麻烦操作系统，直接走CPU内部就搞定了。</p>
<p><code>CAS</code>有三个操作参数：</p>
<ol>
<li>内存位置V(它的值是我们想要去更新的)</li>
<li>预期原值A(上一次从内存中读取的值)</li>
<li>新值B(应该写入的新值)</li>
</ol>
<p><code>CAS</code> 操作过程：将内存位置V的值与A比较(Compare)，如果相等，则说明没有其他线程来修改过这个值，所以把内存V的值更新成B(Swap)，如果不相等，说明V上的值被修改过了，不更新，而是返回当前V的值，再重新执行一次任务再继续这个过程。</p>
<p>所以，当多个线程尝试使用CAS同时更新同一个变量时，其中一个线程会成功更新变量的值，剩下的会失败。失败的线程可以重试或者什么也不做。</p>
<h2 id="JVM对CAS的支持"><a href="#JVM对CAS的支持" class="headerlink" title="JVM对CAS的支持"></a>JVM对CAS的支持</h2><p>在JDK1.5之前，如果不编写明确的代码就无法执行CAS操作，在JDK1.5中引入了底层的支持，在int、long、和对象的引用等类型上都公开了CAS的操作，并且JVM把他们编译为底层硬件提供的最有效的方法，在运行CAS的平台上，运行时把它们编译为相应的机器指令，如果处理器/CPU不支持CAS指令，那么JVM将使用自旋锁。</p>
<p>在原子类变量中，如java.util.concurrent.atomic包下的AtomicXXX，都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作，而在java.util.concurrent中的大多数类在实现时都直接或间接的使用了这些原子变量类。</p>
<p>java.util.concurrent.atomic.AtomicLong源码中的自增getAndIncrement()方法：</p>
<h2 id="CAS缺点"><a href="#CAS缺点" class="headerlink" title="CAS缺点"></a>CAS缺点</h2><p>CAS虽然很高效的解决原子操作，但是CAS仍然存在三大问题。ABA问题，循环时间长开销大和只能保证一个共享变量的原子操作。</p>
<ol>
<li><p>ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化，如果没有发生变化则更新，但是如果一个值原来是A，变成了B，又变成了A，那么使用CAS进行检查时会发现它的值没有发生变化，但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号，每次变量更新的时候把版本号加一，那么A－B－A 就会变成1A-2B－3A。</p>
<p>从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用，并且当前标志是否等于预期标志，如果全部相等，则以原子方式将该引用和该标志的值设置为给定的更新值。</p>
</li>
<li><p>循环时间长开销大。自旋CAS如果长时间不成功，会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升，pause指令有两个作用，第一它可以延迟流水线执行指令（de-pipeline）,使CPU不会消耗过多的执行资源，延迟的时间取决于具体实现的版本，在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突（memory order violation）而引起CPU流水线被清空（CPU pipeline flush），从而提高CPU的执行效率。</p>
</li>
<li><p>只能保证一个共享变量的原子操作。当对一个共享变量执行操作时，我们可以使用循环CAS的方式来保证原子操作，但是对多个共享变量操作时，循环CAS就无法保证操作的原子性，这个时候就可以用锁，或者有一个取巧的办法，就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i＝2,j=a，合并一下ij=2a，然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性，你可以把多个变量放在一个对象里来进行CAS操作。</p>
</li>
<li><p>比较花费CPU资源，即使没有任何争用也会做一些无用功。</p>
</li>
<li><p>会增加程序测试的复杂度，稍不注意就会出现问题。</p>
</li>
</ol>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>可以用CAS在无锁的情况下实现原子操作，但要明确应用场合，非常简单的操作且又不想引入锁可以考虑使用CAS操作，当想要非阻塞地完成某一操作也可以考虑CAS。不推荐在复杂操作中引入CAS，会使程序可读性变差，且难以测试，同时会出现ABA问题。</p>

    </div>

    
    
    

    <footer class="post-footer">
          <div class="post-tags">
              <a href="/myblog/tags/%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C/" rel="tag"># 原子操作</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
            </div>
            <div class="post-nav-item">
                <a href="/myblog/2021/10/31/%E6%9C%AC%E5%8D%9A%E5%AE%A2%E5%8F%82%E8%80%83%E5%86%85%E5%AE%B9/" rel="next" title="本博客参考内容">
                  本博客参考内容 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">矿小测</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/myblog/js/comments.js"></script><script src="/myblog/js/utils.js"></script><script src="/myblog/js/motion.js"></script><script src="/myblog/js/next-boot.js"></script>

  





  





</body>
</html>
